/**
 * @file  MiraMath.h
 * @author Josep Sabater (jsabaterm@el.ub.edu)
 * @date   September, 2015
 * @version 1.0.0
 * @ingroup MiraUtilUBLib
 * @brief  Interface for  several math operations.
 *
 *
 */
#ifndef MIRA_MATH_H
#define MIRA_MATH_H

#include <cmath>
#include <algorithm>  //max, min

namespace MiraUtilUBLib {
   /**
    * @class Math Util
    * @brief Some common utilities related to math operations.
    */
   class Math {
    public:
       /**
        * @brief Absolute difference between two real numbers.
        * @param numA A real number.
        * @param numB A real number.
        * @return A positive real value.
        */
       static inline double diff(const double numA,
                                 const double numB) {
           return std::fabs(numA - numB);
       }
       /**
        * @brief Determines if error is greater (Absolute tolerance test)
        * @param numA A real number.
        * @param numB A real number.
        * @param absTol A real number (absolute tolerance).
        * @return True if error is greater. Otherwise, False.
        * 
        * Use it ONLY if you are comparing against zero as relative 
        * comparison is problematic if either value is zero.
        */
       static inline bool errorGreater(const double numA,
                                       const double numB,
                                       const double absTol) {
           return(std::fabs(numA - numB) > absTol);
       }
       
       /**
        * @brief Checks if two values are close enough.
        * @param a real number.
        * @param b real number.
        * @param relTol relative tolerance.
        * @param absTol absolute tolerance.
        * @return True if both values are close. False otherwise.
        * 
        * See PEP 485 -- A Function for testing approximate equality.
        * 
        * Relative tolerance is the amount of error allowed, relative to the 
        * larger absolute value of a or b. For example, to set a tolerance of 5%, 
        * pass tol=0.05. The default tolerance is 1e-9, which assures that the 
        * two values are the same within about 9 decimal digits. 
        * relTol must be greater than 0.0.
        * 
        * Behaviour near zero: the absolute tolerance value will be used 
        * primarily for comparing to zero. Values around 1-e8 were suggested, 
        * being about half of floating point precision for values of 
        * around value 1.
        * 
        */
       static inline bool isClose(const double a, 
                                  const double b,
                                  const double relTol=1e-09, 
                                  const double absTol=0.0) {
           return (std::fabs(a-b) <= std::max(relTol * std::max(std::fabs(a), std::fabs(b)), absTol));
       }
       
       /**
        * @brief A real number rounded to a given number of digits after the decimal point.
        * @param num A real number.
        * @param dDigits Number of decimal digits to round to.
        * @return A real number rounded to the given decimal digits.
        */
       static inline double round(const double num,
                                  const int dDigits) {
           double myPow = pow(10, dDigits);
           return ceil( (num * myPow) - 0.5 ) / myPow;
       }
       /**
        * @brief Transforms angles in degrees into radians.
        * @param deg A real number expressing degrees.
        * @return A real number expressed in radians.
        */
       static inline double degreeToRadian(const double deg) { return(deg * M_PI /180.0); }
       /**
        * @brief Transforms angles in radians to degrees.
        * @param deg A real number expressing radians.
        * @return A real number expressed in degree.
        */
       static inline double radianToDegree(const double rad) { return(rad * 180.0 / M_PI); }
       /**
        * @brief Determines the min. value among the two given
        * @param a real value.
        * @param b real value.
        * @return A real number.
        */       
       static inline double minValue(const double &numA, const double numB) {  
           return ( (numA<numB) ? numA : numB ); 
       }       
       /**
        * @brief Determines a (weak) random value between the two values provided
        * @param a real value.
        * @param b real value.
        * @return A real number.
        * 
        * This algorithm is biased, so use it at your own risk!
        */       
       static inline double randomDouble(const double numA, const double numB) {           
           double x = (double)rand() / RAND_MAX;
           return std::min(numA, numB) + x * Math::diff(numA, numB);                                
       }
       /**
        * @brief Returns an integer random value between two integer values
        * @param a unsigned integer value.
        * @param b unsigned integer value.
        * @return An unsigned integer number.
        */       
       static inline unsigned int randomUInt(const unsigned int numA, const unsigned int numB) {
           return std::min(numA, numB) + rand()%(abs(numA + numB)+1);                                
       }       
   };
}

#endif
